﻿namespace LuckyAssetManagerContrib
{
	using System;
	using System.Collections.Generic;
	using System.IO;
	using System.Linq;
	using System.Text.RegularExpressions;
	using System.Web;

	using Lucky.AssetManager.Assets;

	using Resources;

	/// <summary>
	/// Utility methods
	/// </summary>
	public static class Utils
	{
		/// <summary>
		/// CSS-file extension
		/// </summary>
		private const string CSS_FILE_EXTENSION = ".css";

		/// <summary>
		/// JavaScript-file extension
		/// </summary>
		private const string JS_FILE_EXTENSION = ".js";

		/// <summary>
		/// Regular expression to determine whether
		/// asset is CSS-file based on its extension
		/// </summary>
		private static readonly Regex _cssFileExtensionRegex = new Regex(@"\.css$", 
			RegexOptions.IgnoreCase | RegexOptions.Compiled);

		/// <summary>
		/// Regular expression to determine whether asset is 
		/// minified version of CSS-file with *.min.css extension
		/// </summary>
		private static readonly Regex _cssFileWithMinExtensionRegex = new Regex(@"\.min\.css$",
			RegexOptions.IgnoreCase | RegexOptions.Compiled);

		/// <summary>
		/// Regular expression to determine whether 
		/// asset is JS-file based on its extension
		/// </summary>
		private static readonly Regex _jsFileExtensionRegex = new Regex(@"\.js$",
			RegexOptions.IgnoreCase | RegexOptions.Compiled);

		/// <summary>
		/// Regular expression to determine whether asset is 
		/// debug version of JS-file with *.debug.js extension
		/// </summary>
		private static readonly Regex _jsFileWithDebugExtensionRegex = new Regex(@"\.debug\.js$",
			RegexOptions.IgnoreCase | RegexOptions.Compiled);

		/// <summary>
		/// Regular expression to determine whether asset is 
		/// minified version of JS-file with *.min.js extension
		/// </summary>
		private static readonly Regex _jsFileWithMinExtensionRegex = new Regex(@"\.min\.js$",
			RegexOptions.IgnoreCase | RegexOptions.Compiled);

		/// <summary>
		/// Regular expression to find first slash
		/// </summary>
		private static readonly Regex _firstSlashRegExp = new Regex(@"^(\/|\\)*",
			RegexOptions.IgnoreCase | RegexOptions.Compiled);

		/// <summary>
		/// Regular expression to find last slash
		/// </summary>
		private static readonly Regex _lastSlashRegExp = new Regex(@"(\/|\\)*$",
			RegexOptions.IgnoreCase | RegexOptions.Compiled);

		/// <summary>
		/// Regular expression for determine protocol in URL
		/// </summary>
		private static readonly Regex _protocolRegExp = new Regex(@"^(https?|ftp)\://",
			RegexOptions.IgnoreCase | RegexOptions.Compiled);

		/// <summary>
		/// Checks whether a asset is an JS-file
		/// </summary>
		/// <param name="asset">Asset</param>
		/// <returns>Result of checking</returns>
		public static bool IsJavaScriptAsset(IAsset asset)
		{
			return IsJavaScriptAsset(asset.Path);
		}

		/// <summary>
		/// Checks whether a asset is an JS-file
		/// </summary>
		/// <param name="assetPath">Asset file path</param>
		/// <returns>Result of checking</returns>
		public static bool IsJavaScriptAsset(string assetPath)
		{
			return _jsFileExtensionRegex.IsMatch(assetPath);
		}

		/// <summary>
		/// Checks whether a asset is an CSS-file
		/// </summary>
		/// <param name="asset">Asset</param>
		/// <returns>Result of checking</returns>
		public static bool IsCssAsset(IAsset asset)
		{
			return IsCssAsset(asset.Path);
		}

		/// <summary>
		/// Checks whether a asset is an CSS-file
		/// </summary>
		/// <param name="assetPath">Asset file path</param>
		/// <returns>Result of checking</returns>
		public static bool IsCssAsset(string assetPath)
		{
			return _cssFileExtensionRegex.IsMatch(assetPath);
		}

		/// <summary>
		/// Removes additional file extension from path of the specified CSS-file
		/// </summary>
		/// <param name="assetPath">CSS-asset file path</param>
		/// <returns>CSS-asset file path without additional file extension</returns>
		public static string RemoveAdditionalCssFileExtension(string assetPath)
		{
			string newAssetPath = _cssFileWithMinExtensionRegex.Replace(assetPath, CSS_FILE_EXTENSION);

			return newAssetPath;
		}

		/// <summary>
		/// Removes additional file extension from path of the specified JS-file
		/// </summary>
		/// <param name="assetPath">JS-asset file path</param>
		/// <returns>JS-asset file path without additional file extension</returns>
		public static string RemoveAdditionalJsFileExtension(string assetPath)
		{
			string newAssetPath = _jsFileWithDebugExtensionRegex.Replace(assetPath, JS_FILE_EXTENSION);
			newAssetPath = _jsFileWithMinExtensionRegex.Replace(newAssetPath, JS_FILE_EXTENSION);

			return newAssetPath;
		}

		/// <summary>
		/// Processes back slashes in URL
		/// </summary>
		/// <param name="url">URL</param>
		/// <returns>Processed URL</returns>
		public static string ProcessBackSlashesInUrl(string url)
		{
			if (String.IsNullOrWhiteSpace(url))
			{
				throw new ArgumentException(Strings.Common_ValueIsEmpty, "url");
			}

			string result = url.Trim().Replace(@"\", @"/");

			return result;
		}

		/// <summary>
		/// Removes first slash from URL
		/// </summary>
		/// <param name="url">URL</param>
		/// <returns>URL without the first slash</returns>
		public static string RemoveFirstSlashFromUrl(string url)
		{
			if (String.IsNullOrWhiteSpace(url))
			{
				throw new ArgumentException(Strings.Common_ValueIsEmpty, "url");
			}

			string result = _firstSlashRegExp.Replace(url.Trim(), String.Empty);

			return result;
		}

		/// <summary>
		/// Removes last slash from URL
		/// </summary>
		/// <param name="url">URL</param>
		/// <returns>URL without the last slash</returns>
		public static string RemoveLastSlashFromUrl(string url)
		{
			if (String.IsNullOrWhiteSpace(url))
			{
				throw new ArgumentException(Strings.Common_ValueIsEmpty, "url");
			}

			string result = _lastSlashRegExp.Replace(url.Trim(), String.Empty);

			return result;
		}

		/// <summary>
		/// Combines two URLs
		/// </summary>
		/// <param name="baseUrl">The base URL</param>
		/// <param name="relativeUrl">The relative URL to add to the base URL</param>
		/// <returns>The absolute URL</returns>
		public static string CombineUrls(string baseUrl, string relativeUrl)
		{
			string result = RemoveLastSlashFromUrl(baseUrl) + "/" + RemoveFirstSlashFromUrl(relativeUrl);

			return result;
		}

		/// <summary>
		/// Transforms relative URL to an absolute (relative to web application)
		/// </summary>
		/// <param name="baseUrl">The base URL</param>
		/// <param name="relativeUrl">The relative URL</param>
		/// <returns>The absolute URL</returns>
		internal static string TransformRelativeUrlToAbsolute(string baseUrl, string relativeUrl)
		{
			if (String.IsNullOrWhiteSpace(baseUrl))
			{
				throw new ArgumentException(Strings.Common_ValueIsEmpty, "baseUrl");
			}

			if (String.IsNullOrWhiteSpace(relativeUrl))
			{
				throw new ArgumentException(Strings.Common_ValueIsEmpty, "relativeUrl");
			}

			string newRelativeUrl = ProcessBackSlashesInUrl(relativeUrl);

			if (newRelativeUrl.StartsWith("/") || _protocolRegExp.IsMatch(newRelativeUrl))
			{
				return newRelativeUrl;
			}

			if (newRelativeUrl.StartsWith("~/"))
			{
				return VirtualPathUtility.ToAbsolute(newRelativeUrl);
			}

			string absoluteUrl;
			string newBaseUrl = ProcessBackSlashesInUrl(
				VirtualPathUtility.ToAbsolute(Path.GetDirectoryName(baseUrl)) + @"/");

			if (newRelativeUrl.StartsWith("../"))
			{
				const string fakeSiteUrl = "http://lamcontrib.codeplex.com/";
				var baseUri = new Uri(CombineUrls(fakeSiteUrl, newBaseUrl), UriKind.Absolute);

				var absoluteUri = new Uri(baseUri, newRelativeUrl);
				absoluteUrl = absoluteUri.PathAndQuery;
			}
			else
			{
				absoluteUrl = CombineUrls(newBaseUrl, newRelativeUrl);
			}

			return absoluteUrl;
		}

		/// <summary>
		/// Checks existance of specified file
		/// </summary>
		/// <param name="filePath">File path</param>
		/// <returns>Result of checking (true – exist; false – not exist)</returns>
		public static bool CheckFileExist(string filePath)
		{
			HttpServerUtility server = HttpContext.Current.Server;

			return File.Exists(server.MapPath(filePath));
		}

		/// <summary>
		/// Gets date and time of last modification files
		/// </summary>
		/// <param name="filePaths">List of file paths</param>
		/// <returns>Date and time of last modification files</returns>
		public static DateTime GetLastModifyTimeOfFiles(string[] filePaths)
		{
			var lastModifyDates = new List<DateTime>();

			foreach (string filePath in filePaths)
			{
				if (File.Exists(filePath))
				{
					lastModifyDates.Add(File.GetLastWriteTime(filePath));
				}
			}

			DateTime lastModifyDate = lastModifyDates.Max();

			return lastModifyDate;
		}

		/// <summary>
		/// Converts value of type DateTime to hash string
		/// </summary>
		/// <param name="dateTime">Date and time</param>
		/// <returns>Hash string</returns>
		public static string ConvertDateTimeToHashString(DateTime dateTime)
		{
			var ts = new TimeSpan(dateTime.Ticks);
			var ms = (long)Math.Round(ts.TotalMilliseconds, 0);
			string hash = ms.ToString("x");

			return hash;
		}
	}
}